package com.alibaba.cloud.application;

import com.alibaba.cloud.application.bankaccount.*;
import com.alibaba.cloud.application.banktransfer.BankTransferCreatedEvent;
import com.alibaba.cloud.application.banktransfer.MarkBankTransferCompletedCommand;
import com.alibaba.cloud.application.banktransfer.MarkBankTransferFailedCommand;
import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.callbacks.LoggingCallback;
import org.axonframework.modelling.saga.EndSaga;
import org.axonframework.modelling.saga.SagaEventHandler;
import org.axonframework.modelling.saga.StartSaga;
import org.axonframework.spring.stereotype.Saga;
import org.springframework.beans.factory.annotation.Autowired;

import static org.axonframework.commandhandling.GenericCommandMessage.asCommandMessage;

@Saga
public class BankTransferManagementSaga {

	private transient CommandBus commandBus;

	@Autowired
	public void setCommandBus(CommandBus commandBus) {
		this.commandBus = commandBus;
	}

	private String sourceBankAccountId;
	private String destinationBankAccountId;
	private long amount;

	@StartSaga
	@SagaEventHandler(associationProperty = "bankTransferId")
	public void on(BankTransferCreatedEvent event) {
		this.sourceBankAccountId = event.getSourceBankAccountId();
		this.destinationBankAccountId = event.getDestinationBankAccountId();
		this.amount = event.getAmount();

		DebitSourceBankAccountCommand command = new DebitSourceBankAccountCommand(event.getSourceBankAccountId(),
				event.getBankTransferId(), event.getAmount());
		commandBus.dispatch(asCommandMessage(command), LoggingCallback.INSTANCE);
	}

	@SagaEventHandler(associationProperty = "bankTransferId")
	@EndSaga
	public void on(SourceBankAccountNotFoundEvent event) {
		MarkBankTransferFailedCommand markFailedCommand = new MarkBankTransferFailedCommand(event.getBankTransferId());
		commandBus.dispatch(asCommandMessage(markFailedCommand), LoggingCallback.INSTANCE);
	}

	@SagaEventHandler(associationProperty = "bankTransferId")
	@EndSaga
	public void on(SourceBankAccountDebitRejectedEvent event) {
		MarkBankTransferFailedCommand markFailedCommand = new MarkBankTransferFailedCommand(event.getBankTransferId());
		commandBus.dispatch(asCommandMessage(markFailedCommand), LoggingCallback.INSTANCE);
	}

	@SagaEventHandler(associationProperty = "bankTransferId")
	public void on(SourceBankAccountDebitedEvent event) {
		CreditDestinationBankAccountCommand command = new CreditDestinationBankAccountCommand(destinationBankAccountId,
				event.getBankTransferId(), event.getAmount());
		commandBus.dispatch(asCommandMessage(command), LoggingCallback.INSTANCE);
	}

	@SagaEventHandler(associationProperty = "bankTransferId")
	@EndSaga
	public void on(DestinationBankAccountNotFoundEvent event) {
		ReturnMoneyOfFailedBankTransferCommand returnMoneyCommand = new ReturnMoneyOfFailedBankTransferCommand(
				sourceBankAccountId, amount);
		commandBus.dispatch(asCommandMessage(returnMoneyCommand), LoggingCallback.INSTANCE);

		MarkBankTransferFailedCommand markFailedCommand = new MarkBankTransferFailedCommand(event.getBankTransferId());
		commandBus.dispatch(asCommandMessage(markFailedCommand), LoggingCallback.INSTANCE);
	}

	@EndSaga
	@SagaEventHandler(associationProperty = "bankTransferId")
	public void on(DestinationBankAccountCreditedEvent event) {
		MarkBankTransferCompletedCommand command = new MarkBankTransferCompletedCommand(event.getBankTransferId());
		commandBus.dispatch(asCommandMessage(command), LoggingCallback.INSTANCE);
	}
}
